﻿/**
 * Peter
 * Created by: Peter Development Team
 *    http://peter.codeplex.com/
 * 
 * GNU General Public License version 2 (GPLv2)
 *    http://peter.codeplex.com/license
 *
 *  This code is provided on an AS IS basis, with no WARRANTIES,
 *  CONDITIONS or GUARANTEES of any kind.
 *  
 **/

using System;
using System.ComponentModel.Composition;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using Peter.Common;
using Peter.Common.BindingConverters;
using Peter.Common.Icons;
using Peter.Common.Interfaces;
using Peter.Common.MainMenu;
using Peter.Common.Utilities;

namespace Peter.Ruby
{
   /// <summary>
   /// Rails console class.
   /// </summary>
   public class RailsConsole : ViewModelBase
   {
      private IPeter m_Peter;
      private bool m_IsServerRunning;
      private string m_RailsServerPid;
      private string m_RailsServerText;
      private readonly CommandPrompt m_BgCmd;
      private readonly CommandPrompt m_ServerCmd;
      private MainMenuIcon m_RailsServerIcon;
      private RelayCommand m_CmdRunRailsServer;
      private RelayCommand m_CmdRunRailsConsole;
      private PeterContextMenuItem m_RunRailsServerContextMenuItem;
      private PeterContextMenuItem m_RunRailsConsoleContextMenuItem;
      private readonly StringBuilder m_BgCmdOutput;

      private const string RUN_SERVER = "Run Rails Server";
      private const string STOP_SERVER = "Stop Rails Server";
      private const string START_SERVER = "Starting Rails Server...";

      /// <summary>
      /// Initializes a new rails console.
      /// </summary>
      public RailsConsole ()
      {
         this.RailsServerText = RUN_SERVER;
         this.RailsServerIcon = MainMenuIcon.Play;

         this.m_BgCmdOutput = new StringBuilder();
         this.m_BgCmd = new CommandPrompt ();
         this.m_BgCmd.OutputRecieved += this.OnBgCmdOutput;
         this.m_BgCmd.Initialize ();

         this.m_ServerCmd = new CommandPrompt ();
         this.m_ServerCmd.Initialize ();
         this.m_ServerCmd.OutputRecieved += this.OnRailsServerOutput;
      }

      /// <summary>
      /// Gets the instance of peter.
      /// </summary>
      [Import (typeof (IPeter))]
      public IPeter Peter
      {
         get { return this.m_Peter; }
         set
         {
            this.m_Peter = value;
            this.m_Peter.ShuttingDown += this.OnPeterShutdown;
         }
      }

      /// <summary>
      /// Gets or Sets the icon to display on the run server command.
      /// </summary>
      public MainMenuIcon RailsServerIcon
      {
         get { return this.m_RailsServerIcon; }
         set
         {
            this.m_RailsServerIcon = value;
            this.RaisePropertyChanged ("RailsServerIcon");
         }
      }

      /// <summary>
      /// Gets or Sets the text to display on the run server command.
      /// </summary>
      public string RailsServerText
      {
         get { return this.m_RailsServerText; }
         set
         {
            this.m_RailsServerText = value;
            this.RaisePropertyChanged ("RailsServerText");
         }
      }

      /// <summary>
      /// Gets the command to run the rails server.
      /// </summary>
      public ICommand RunRailsServerCommand
      {
         get
         {
            return this.m_CmdRunRailsServer ?? (this.m_CmdRunRailsServer =
               new RelayCommand (this.OnRunRailsServer, this.CanRunRailsServer));
         }
      }

      /// <summary>
      /// Gets the command to run the rails console.
      /// </summary>
      public ICommand RunRailsConsoleCommand
      {
         get
         {
            return this.m_CmdRunRailsConsole ?? (this.m_CmdRunRailsConsole =
               new RelayCommand (this.OnRunRailsConsole));
         }
      }

      private void OnRunRailsConsole (object obj)
      {
      }

      /// <summary>
      /// Checks to see if the rails server can be ran.
      /// </summary>
      /// <param name="obj">Command parameter.</param>
      /// <returns>True if able to run command, otherwise false.</returns>
      private bool CanRunRailsServer (object obj)
      {
         if (this.RailsServerText == RUN_SERVER)
         {
            if (obj == null) return false;
            var menuItem = obj as PeterContextMenuItem;
            return menuItem != null && menuItem.DataContext != null;
         }
         if (this.RailsServerText == STOP_SERVER)
            return !string.IsNullOrEmpty (this.m_RailsServerPid);
         return false;
      }

      /// <summary>
      /// Occurs when our background command prompt receives output.
      /// </summary>
      /// <param name="message">Message from process.</param>
      /// <param name="isError">True if message is an error, otherwise false.</param>
      private void OnBgCmdOutput (string message, bool isError)
      {
         this.m_BgCmdOutput.AppendLine (message);
      }

      /// <summary>
      /// Occurs when output is received from the rails server process.
      /// </summary>
      /// <param name="message">Message from process.</param>
      /// <param name="isError">True if message is an error, otherwise false.</param>
      private void OnRailsServerOutput (string message, bool isError)
      {
         if (message.Contains ("WEBrick") && message.Contains ("pid="))
         {
            this.m_IsServerRunning = true;
            this.RailsServerText = STOP_SERVER;
            this.RailsServerIcon = MainMenuIcon.Stop;
            var start = message.IndexOf ("pid=", StringComparison.Ordinal) + 4;
            var end = message.IndexOf (' ', start);
            this.m_RailsServerPid = message.Substring (start, end - start);
         }
         if (isError)
            Console.Error.WriteLine (message);
         else
            Console.WriteLine (message);
      }

      /// <summary>
      /// Runs the rails server.
      /// </summary>
      /// <param name="obj"></param>
      private void OnRunRailsServer (object obj)
      {
         if (this.m_IsServerRunning)
         {
            this.m_BgCmd.RunCommand (string.Format ("taskkill /pid {0} /F", this.m_RailsServerPid));
            this.RailsServerIcon = MainMenuIcon.Play;
            this.m_RailsServerPid = string.Empty;
            this.RailsServerText = RUN_SERVER;
            this.m_IsServerRunning = false;
         }
         else
         {
            var menuItem = obj as PeterContextMenuItem;
            if (menuItem != null)
            {
               var project = menuItem.DataContext as PeterProject;
               if (project != null)
               {
                  this.RailsServerText = START_SERVER;
                  this.RailsServerIcon = MainMenuIcon.LoadingHourglass;
                  this.m_ServerCmd.ChangeDirectory (project.ProjectLocation);
                  this.m_ServerCmd.RunCommand ("rails server");
               }
            }
         }
      }

      /// <summary>
      /// Gets the run server menu item.
      /// </summary>
      [Export ("WorkspaceMenuItem")]
      public PeterContextMenuItem RunRailsServerContextMenuItem
      {
         get
         {
            if (this.m_RunRailsServerContextMenuItem == null)
            {
               var icon = new IconDisplay ();
               icon.SetBinding (IconDisplay.IconProperty,
                  new Binding ("RailsServerIcon") { Source = this });
               this.m_RunRailsServerContextMenuItem = new PeterContextMenuItem
               {
                  Priority = 100,
                  Icon = icon,
                  Command = this.RunRailsServerCommand
               };
               this.m_RunRailsServerContextMenuItem.SetBinding (HeaderedItemsControl.HeaderProperty,
                  new Binding ("RailsServerText") { Source = this });
               this.m_RunRailsServerContextMenuItem.CommandParameter = this.m_RunRailsServerContextMenuItem;
               this.m_RunRailsServerContextMenuItem.SetBinding (UIElement.VisibilityProperty,
                  new Binding { Converter = new TypeVisibilityConverter <RailsProject> () });
            }
            return this.m_RunRailsServerContextMenuItem;
         }
      }

      /// <summary>
      /// Gets the run console menu item.
      /// </summary>
      [Export ("WorkspaceMenuItem")]
      public PeterContextMenuItem RunRailsConsoleContextMenuItem
      {
         get
         {
            if (this.m_RunRailsConsoleContextMenuItem == null)
            {
               this.m_RunRailsConsoleContextMenuItem = new PeterContextMenuItem
               {
                  Priority = 101,
                  Title = "Start a Rails Console",
                  Icon = MainMenuIcon.Ssh,
                  Command = this.RunRailsConsoleCommand
               };
               this.m_RunRailsConsoleContextMenuItem.CommandParameter = this.m_RunRailsConsoleContextMenuItem;
               this.m_RunRailsConsoleContextMenuItem.SetBinding (UIElement.VisibilityProperty,
                  new Binding { Converter = new TypeVisibilityConverter <RailsProject> () });
            }
            return this.m_RunRailsConsoleContextMenuItem;
         }
      }

      /// <summary>
      /// Called when Peter is shutting down.
      /// </summary>
      /// <param name="sender">Peter object.</param>
      /// <param name="e">EventArgs</param>
      private void OnPeterShutdown (object sender, EventArgs e)
      {
         if (this.m_IsServerRunning && !string.IsNullOrEmpty (this.m_RailsServerPid))
            this.m_BgCmd.RunCommand (string.Format ("taskkill /pid {0} /F", this.m_RailsServerPid));
         this.m_ServerCmd.Close ();
         this.m_BgCmd.Close ();
      }
   }
}
